#pragma once

#include <sstream>
#include <iterator>
#include <boost/spirit/core.hpp>
#include <boost/spirit/attribute.hpp>
#include <boost/spirit/utility/confix.hpp>
#include <boost/spirit/phoenix/binders.hpp>
#include <boost/spirit/iterator/multi_pass.hpp>
#include <boost/range.hpp>
#include "json.hpp"

namespace alpha { namespace rpc {

namespace detail {

template <typename Container, typename Item>
void push_back_aux(Container& c, Item const& item)
{
	c.template reference<typename Container::array>().push_back(item);
}

template <typename Container, typename Pair>
void insert_aux(Container& c, Pair const &p)
{
	typedef typename Container::object object;
	object &obj = c.template reference<object>();
	obj[p.first] = p.second;
}

template <typename String, typename Iterator>
String string_aux(Iterator beg, Iterator end)
{
	std::basic_ostringstream<typename String::value_type> ss;
	for (Iterator i = beg; i != end; ++i)
	{
		if (*i != '\\')
		{
			ss << *i;
			continue;
		}
		switch (*++i)
		{
			case 'b':
				ss << '\b';
				break;
			case 'f':
				ss << '\f';
				break;
			case 'n':
				ss << '\n';
				break;
			case 'r':
				ss << '\r';
				break;
			case 't':
				ss << '\t';
				break;
			// TODO
			//case 'u':
			//	break;
			case '"':
			case '\\':
			case '/':
			default:
				ss << *i;
				break;
		}
	}
	return ss.str();
}

template <typename Json>
struct json_closure : boost::spirit::closure<json_closure<Json>, Json>
{
	typename json_closure::member1 val;
};

template <typename Json>
struct string_closure : boost::spirit::closure<string_closure<Json>, typename Json::string>
{
	typename string_closure::member1 val;
};

template <typename Json>
struct pair_closure : boost::spirit::closure<pair_closure<Json>, std::pair<typename Json::string, Json> >
{
	typename pair_closure::member1 val;
};

template <typename Json>
struct json_grammer : public boost::spirit::grammar<json_grammer<Json>, typename json_closure<Json>::context_t>
{
	template <typename ScannerT>
	struct definition
	{
		typedef typename Json::null null_t;
		typedef typename Json::number number_t;
		typedef typename Json::string string_t;
		typedef typename Json::array array_t;
		typedef typename Json::object object_t;
		typedef std::pair<string_t, Json> pair_t;

		definition(json_grammer const& self)
		{
			using boost::spirit::anychar_p;
			using boost::spirit::ch_p;
			using boost::spirit::str_p;
			using boost::spirit::confix_p;
			typedef boost::spirit::uint_parser<short, 16, 4, 4> hex4_p;
			typedef boost::spirit::real_parser<number_t, boost::spirit::real_parser_policies<number_t> > number_p;

			using phoenix::arg1;
			using phoenix::arg2;
			using phoenix::bind;
			using phoenix::construct_;

			top
				=	object [ self.val = arg1 ]
				;

			object
				=	ch_p('{') [ object.val = object_t() ]  >> !members >> '}'
				;

			members
				=	pair [ bind(&insert_aux<Json, pair_t>)(object.val, arg1) ] % ','
				;

			pair
				=	string [ bind(&pair_t::first)(pair.val) = arg1 ] >> ':' >> value [ bind(&pair_t::second)(pair.val) = arg1 ] 
				;

			array
				=	ch_p('[') [ array.val = array_t() ] >> !elements >> ']'
				;

			elements
				=	value [ bind(&push_back_aux<Json, Json>)(array.val, arg1) ] % ','
				;

			value
				=	str_p("null")  [ value.val = null_t() ]
				|	str_p("true")  [ value.val = true ]
				|	str_p("false") [ value.val = false ]
				|	number         [ value.val = arg1 ]
				|	string         [ value.val = arg1 ]
				|	array          [ value.val = arg1 ]
				|	object         [ value.val = arg1 ]
				;

			string
				=	confix_p('"', (*(anychar_p - ch_p('\\') | ch_p('\\') >> (ch_p('u') >> hex4_p() | anychar_p - ch_p('u'))))
						[ string.val = bind(&detail::string_aux<string_t, typename ScannerT::iterator_t>)(arg1, arg2) ], '"')
				;

			number
				=	number_p()        [ number.val = arg1 ]
				;
			
			//BOOST_SPIRIT_DEBUG_NODE(object);
			//BOOST_SPIRIT_DEBUG_NODE(members);
			//BOOST_SPIRIT_DEBUG_NODE(pair);
			//BOOST_SPIRIT_DEBUG_NODE(array);
			//BOOST_SPIRIT_DEBUG_NODE(elements);
			//BOOST_SPIRIT_DEBUG_NODE(value);
			//BOOST_SPIRIT_DEBUG_NODE(string);
			//BOOST_SPIRIT_DEBUG_NODE(number);
		}


        boost::spirit::rule<ScannerT, typename json_closure<Json>::context_t> object, members, array, elements, value, number;
		boost::spirit::rule<ScannerT, typename string_closure<Json>::context_t> string;
		boost::spirit::rule<ScannerT, typename pair_closure<Json>::context_t> pair;
		boost::spirit::rule<ScannerT> top;

		boost::spirit::rule<ScannerT> const&
		start() const { return top; }
	};
};

} // namespace detail

template <typename Stream, template <typename> class Tmpl>
Stream & operator >>(Stream &stream, basic_json<Tmpl> &v)
{
	detail::json_grammer<basic_json<Tmpl> > g;
	//BOOST_SPIRIT_DEBUG_NODE(g);
	typedef typename Stream::char_type char_type;
	typedef std::istreambuf_iterator<char_type> input_iterator;
	typedef boost::spirit::multi_pass<input_iterator> iterator;
	boost::spirit::parse_info<iterator> info
		= boost::spirit::parse(iterator(input_iterator(stream)), iterator(), g[phoenix::var(v) = phoenix::arg1], boost::spirit::space_p);
	if (!info.full)
		stream.setstate(std::ios::failbit);
	return stream;
}

template <typename Iterator, template <typename> class Tmpl>
bool operator >>(boost::iterator_range<Iterator> &range, basic_json<Tmpl> &v)
{
	detail::json_grammer<basic_json<Tmpl> > g;
	typedef typename boost::iterator_range<Iterator>::iterator iterator;
	boost::spirit::parse_info<iterator> info
		= boost::spirit::parse(range.begin(), range.end(), g[phoenix::var(v) = phoenix::arg1], boost::spirit::space_p);
	return info.full;
}

}} // namespace alpha::rpc
